package vertxblog.api;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.JWTOptions;
import io.vertx.ext.auth.PubSecKeyOptions;
import io.vertx.ext.auth.authentication.AuthenticationProvider;
import io.vertx.ext.auth.authorization.AuthorizationProvider;
import io.vertx.ext.auth.authorization.PermissionBasedAuthorization;
import io.vertx.ext.auth.jwt.JWTAuth;
import io.vertx.ext.auth.jwt.JWTAuthOptions;
import io.vertx.ext.auth.jwt.authorization.JWTAuthorization;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.*;
import vertxblog.constants.Constant;
import vertxblog.constants.Key;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.*;

public class ApiVerticle extends AbstractVerticle {

    @Override
    public void start(Promise<Void> startPromise) throws Exception {
        Set<String> allowedHeaders = new HashSet<>();
        allowedHeaders.add("x-requested-with");
        allowedHeaders.add("Access-Control-Allow-Origin");
        allowedHeaders.add("Access-Control-Allow-Headers");
        allowedHeaders.add("origin");
        allowedHeaders.add("Content-Type");
        allowedHeaders.add("Authorization");
        allowedHeaders.add("accept");
        allowedHeaders.add("X-PINGARUNER");
        Set<HttpMethod> allowedMethods = new HashSet<>();
        allowedMethods.add(HttpMethod.GET);
        allowedMethods.add(HttpMethod.POST);
        allowedMethods.add(HttpMethod.OPTIONS);
        allowedMethods.add(HttpMethod.DELETE);
        allowedMethods.add(HttpMethod.PATCH);
        allowedMethods.add(HttpMethod.PUT);

        // 创建http服务
        HttpServer server = vertx.createHttpServer();
        // 创建路由
        Router router = Router.router(vertx);
        // 开启body json解析支持
        router.route().handler(BodyHandler.create());
        // 允许跨域
        router.route().handler(CorsHandler.create("*").allowedHeaders(allowedHeaders).allowedMethods(allowedMethods));
        // 处理超时
        router.route().handler(TimeoutHandler.create(3000));
        // 处理错误
        router.route().failureHandler(r -> {
            this.returnJson(r, r.statusCode(), r.failure().getMessage());
        });


        // jwt
        JWTAuthOptions config = new JWTAuthOptions().addPubSecKey(new PubSecKeyOptions()
            .setAlgorithm("HS256")
            .setBuffer(Constant.JWT_SECRET));
        JWTAuth jwt = JWTAuth.create(vertx, config);


        // 配置授权
        JWTAuthHandler authHandler = JWTAuthHandler.create(jwt);
        AuthorizationProvider authzProvider = JWTAuthorization.create("permissions");
        // 配置所有的保护接口
        // router.route("/api/article/*").handler(authHandler);
        // router.route("/api/category/*").handler(authHandler);
        router.route("/api/article/addart").handler(authHandler);
        router.route("/api/article/updateart").handler(authHandler);
        router.route("/api/article/delete/:id").handler(authHandler);
        router.route("/api/category/addCate").handler(authHandler);
        router.route("/api/category/updateCate").handler(authHandler);
        router.route("/api/category/deleteCate/:id").handler(authHandler);


        // 通过permission来判定接口是否访问
        AuthorizationHandler adminHandler = AuthorizationHandler.create(
                PermissionBasedAuthorization.create("admin"))
            .addAuthorizationProvider(authzProvider);


        // api路由start-----------------------
        // 登录
        router.post("/api/user/login").handler(ctx -> {
            var body = ctx.body().asJsonObject();
            String username = body.getString("username", "");
            String password = body.getString("password", "");

            // 判空
            if (StrUtil.isBlank(username) || StrUtil.isBlank(password)) {
                this.returnJson(ctx, 400, "用户名和密码不能为空");
            }

            // 加密明文密码(使用hutool)
            String encodePwd = DigestUtil.sha256Hex(password);
            // System.out.println(encodePwd);

            HashMap<String, String> msg = new HashMap<>();
            msg.put("username", username);
            msg.put("password", encodePwd);
            vertx.eventBus().<JsonObject>request(Key.USER_LOGIN, msg, res -> {
                if (res.succeeded()) {
                    var userInfo = res.result().body();

                    String token = jwt.generateToken(
                        new JsonObject()
                            .put("issuer", "tingyu")
                            .put("userid", userInfo.getLong("id"))
                            .put("username", userInfo.getString("username"))
                            .put("permissions", new JsonArray().add("admin")),
                        new JWTOptions().setExpiresInMinutes(120));

                    JsonObject data = new JsonObject();
                    data.put("user", userInfo);
                    data.put("token", token);
                    this.returnJson(ctx, 200, "success", data);
                } else {
                    this.returnJson(ctx, 500, res.cause().getMessage());
                }
            });
        });
        // 获取全部分类
        router.get("/api/category/getAllList").handler(this::handleGetAllList);
        // 获取一个分类
        router.get("/api/category/getInfo").handler(this::handleGetCategoryDetail);
        // 添加分类
        router.post("/api/category/addCate").handler(this::handleAddCate);
        // 编辑分类
        router.put("/api/category/updateCate").handler(this::handleUpdateCate);
        // 删除分类
        router.delete("/api/category/deleteCate/:id").handler(this::handleDeleteCate);
        // 首页文章列表(含文章搜索)
        router.get("/api/article/getList").handler(this::handleGetHomeArticleList);
        // 文章详情
        router.get("/api/article/getInfo").handler(this::handleGetArticleDetail);
        // 文章归档
        router.get("/api/article/getListByClass").handler(this::handleGetArticleArchive);
        // 添加文章
        router.post("/api/article/addart").handler(adminHandler).handler(this::handleAddArticle);
        // 编辑文章
        router.put("/api/article/updateart").handler(adminHandler).handler(this::handleEditArticle);
        // 删除文章
        router.delete("/api/article/delete/:id").handler(adminHandler).handler(this::handleDeleteArticle);
        // api路由end-----------------------


        // 启动http服务
        server.requestHandler(router)
            .listen(
                config().getInteger("http.port", 8989),
                result -> {
                    if (result.succeeded()) {
                        startPromise.complete();
                    } else {
                        startPromise.fail(result.cause());
                    }
                });

    }




    /**
     * 获取全部分类
     *
     * @param routingContext RoutingContext
     */
    private void handleGetAllList(RoutingContext routingContext) {

        vertx.eventBus().<JsonArray>request(Key.GET_ALL_CATEGORY, null, res -> {
            if (res.succeeded()) {
                this.returnJson(routingContext, 200, "success", res.result().body());
            } else {
                this.returnJson(routingContext, 500, res.cause().getMessage());
            }
        });

    }


    /**
     * 获取一个分类的详情
     *
     * @param routingContext RoutingContext
     */
    private void handleGetCategoryDetail(RoutingContext routingContext) {
        int id = Integer.parseInt(routingContext.request().getParam("id", ""));
        HashMap<String, Integer> msg = new HashMap<>();
        msg.put("id", id);
        vertx.eventBus().<JsonObject>request(Key.GET_CATEGORY_DETAIL, msg, res -> {
            if (res.succeeded()) {
                this.returnJson(routingContext, 200, "success", res.result().body());
            } else {
                this.returnJson(routingContext, 500, res.cause().getMessage());
            }
        });
    }


    /**
     * 添加分类
     * @param routingContext RoutingContext
     */
    private void handleAddCate(RoutingContext routingContext) {
        var body = routingContext.body().asJsonObject();
        String cateName = body.getString("name", "");
        if (StrUtil.isBlank(cateName)) {
            this.returnJson(routingContext, 400, "分类名称不能为空");
        }

        HashMap<String, String> msg = new HashMap<>();
        msg.put("name", cateName);
        vertx.eventBus().<Boolean>request(Key.ADD_CATEGORY, msg, res -> {
            if (res.succeeded() && res.result().body()) {
                this.returnJson(routingContext, 200, "success");
            } else {
                this.returnJson(routingContext, 500, res.cause().getMessage());
            }
        });

    }


    /**
     * 编辑分类
     * @param routingContext` RoutingContext
     */
    private void handleUpdateCate(RoutingContext routingContext) {
        var cate = routingContext.body().asJsonObject();
        String id = cate.getString("id");
        String name = cate.getString("name", "");
        if (StrUtil.isBlank(name)) {
            this.returnJson(routingContext, 400, "分类名称不能为空");
        }

        HashMap<String, String> msg = new HashMap<>();
        msg.put("id", id);
        msg.put("name", name);
        vertx.eventBus().<Boolean>request(Key.UPDATE_CATEGORY, msg, res -> {
            if (res.succeeded() && res.result().body()) {
                this.returnJson(routingContext, 200, "success");
            } else {
                this.returnJson(routingContext, 500, res.cause().getMessage());
            }
        });
    }


    /**
     * 删除分类
     * @param routingContext RoutingContext
     */
    private void handleDeleteCate(RoutingContext routingContext) {
        int id = Integer.parseInt(routingContext.pathParam("id"));
        HashMap<String, Integer> msg = new HashMap<>();
        msg.put("id", id);
        vertx.eventBus().<Boolean>request(Key.DELETE_CATEGORY, msg, res -> {
            if (res.succeeded() && res.result().body()) {
                this.returnJson(routingContext, 200, "success");
            } else {
                this.returnJson(routingContext, 500, res.cause().getMessage());
            }
        });
    }


    /**
     * 获取首页文章列表
     *
     * @param routingContext RoutingContext
     */
    private void handleGetHomeArticleList(RoutingContext routingContext) {
        var req = routingContext.request();
        String keywords = req.getParam("keywords", "");
        String pageSize = req.getParam("pageSize", "6");
        String currentPage = req.getParam("currentPage", "1");

        HashMap<String, String> msg = new HashMap<>();
        msg.put("keywords", keywords);
        msg.put("pageSize", pageSize);
        msg.put("currentPage", currentPage);
        vertx.eventBus().<JsonObject>request(Key.GET_ARTICLE_LIST, msg, res -> {
            if (res.succeeded()) {
                this.returnJson(routingContext, 200, "success", res.result().body());
            } else {
                this.returnJson(routingContext, 500, res.cause().getMessage());
            }
        });
    }


    /**
     * 获取文章详情
     *
     * @param routingContext RoutingContext
     */
    private void handleGetArticleDetail(RoutingContext routingContext) {
        String pid = routingContext.request().getParam("id", "1");
        // 验证大于0的正整数
        String pattern = "^\\+?[1-9][0-9]*$";
        if (!Pattern.matches(pattern, pid)) {
            this.returnJson(routingContext, 400, "无效的id值");
        }

        int id = Integer.parseInt(pid);
        HashMap<String, Integer> msg = new HashMap<>();
        msg.put("id", id);

        vertx.eventBus().<JsonObject>request(Key.GET_ARTICLE_DETAIL, msg, res -> {
            if (res.succeeded()) {
                this.returnJson(routingContext, 200, "success", res.result().body());
            } else {
                this.returnJson(routingContext, 500, res.cause().getMessage());
            }
        });
    }


    /**
     * 获取文章归档
     *
     * @param routingContext RoutingContext
     */
    private void handleGetArticleArchive(RoutingContext routingContext) {
        String classId = routingContext.request().getParam("classId", "");
        if (StrUtil.isEmpty(classId)){  // 没有classId参数

            vertx.eventBus().<JsonObject>request(Key.GET_ARTICLE_ARCHIVE, null, res -> {
                if (res.succeeded()) {
                    this.returnJson(routingContext, 200, "success", res.result().body());
                } else {
                    this.returnJson(routingContext, 500, res.cause().getMessage());
                }
            });

        } else {    // 有参数
            HashMap<String, Integer> msg = new HashMap<>();
            msg.put("classId", Integer.valueOf(classId));
            vertx.eventBus().<JsonObject>request(Key.GET_ARTICLE_ARCHIVE_WITH_CATEID, msg, res -> {
                if (res.succeeded()) {
                    this.returnJson(routingContext, 200, "success", res.result().body());
                } else {
                    this.returnJson(routingContext, 500, res.cause().getMessage());
                }
            });
        }

    }


    /**
     * 添加文章
     *
     * @param routingContext RoutingContext
     */
    private void handleAddArticle(RoutingContext routingContext) {
        JsonObject art = routingContext.body().asJsonObject();
        String title = art.getString("title", "");
        int classId = art.getInteger("classId", 0);
        String content = art.getString("content", "");
        if (StrUtil.isBlank(title)) {
            this.returnJson(routingContext, 400, "标题不能为空");
        }
        if (classId <= 0) {
            this.returnJson(routingContext, 400, "请选择分类");
        }

        HashMap<String, String> msg = new HashMap<>();
        msg.put("title", title);
        msg.put("classId", String.valueOf(classId));
        msg.put("content", content);
        vertx.eventBus().<Boolean>request(Key.ADD_ARTICLE, msg, res -> {
            if (res.succeeded() && res.result().body()) {
                this.returnJson(routingContext, 200, "success");
            } else {
                this.returnJson(routingContext, 500, res.cause().getMessage());
            }
        });
    }


    /**
     * 编辑文章
     *
     * @param routingContext RoutingContext
     */
    private void handleEditArticle(RoutingContext routingContext) {
        JsonObject art = routingContext.body().asJsonObject();
        int id = art.getInteger("id", 0);
        String title = art.getString("title", "");
        long classId = art.getInteger("classId", 0);
        String content = art.getString("content", "");
        if (id <= 0) {
            this.returnJson(routingContext, 400, "请检查文章id");
        }
        if (StrUtil.isBlank(title)) {
            this.returnJson(routingContext, 400, "标题不能为空");
        }
        if (classId <= 0) {
            this.returnJson(routingContext, 400, "请选择分类");
        }
        // 检查文章是否存在
        vertx.eventBus().<Boolean>request(Key.GET_ARTICLE_IS_EXIST,
            new HashMap<String, Integer>().put("id", id),
            res -> {
                if (res.succeeded()) {
                    if (!res.result().body()) {  // 文章不存在
                        this.returnJson(routingContext, 404, "文章不存在");
                    } else {
                        // 修改文章操作
                        HashMap<String, String> msg = new HashMap<>();
                        msg.put("id", String.valueOf(id));
                        msg.put("title", title);
                        msg.put("classId", String.valueOf(classId));
                        msg.put("content", content);
                        vertx.eventBus().<Boolean>request(Key.UPDATE_ARTICLE, msg, res2 -> {
                            if (res2.succeeded() && res2.result().body()) {
                                this.returnJson(routingContext, 200, "success");
                            } else {
                                this.returnJson(routingContext, 500, res2.cause().getMessage());
                            }
                        });
                    }

                } else {
                    this.returnJson(routingContext, 500, res.cause().getMessage());
                }
            });

    }


    /**
     * 删除文章
     * @param routingContext RoutingContext
     */
    private void handleDeleteArticle(RoutingContext routingContext) {
        int id = Integer.parseInt(routingContext.pathParam("id"));

        var msg = new HashMap<String, Integer>();
        msg.put("id", id);
        vertx.eventBus().<Boolean>request(Key.DELETE_ARTICLE, msg, res -> {
            if (res.succeeded() && res.result().body()) {
                this.returnJson(routingContext, 200, "sucess");
            } else {
                this.returnJson(routingContext, 500, res.cause().getMessage());
            }
        });
    }










    /**
     * 返回json的方法
     * @param routingContext RoutingContext
     * @param code           int
     * @param msg            String
     */
    private void returnJson(RoutingContext routingContext, int code, String msg) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.put("code", code).put("msg", msg).put("data", null);

        routingContext.response()
            .setStatusCode(code)
            .putHeader("content-type", "application/json; charset=utf-8")
            .end(jsonObject.encodePrettily());
    }

    /**
     * 返回json的方法
     * @param routingContext RoutingContext
     * @param code           int
     * @param msg            String
     * @param data           JsonObject
     */
    private void returnJson(RoutingContext routingContext, int code, String msg, JsonObject data) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.put("code", code).put("msg", msg).put("data", data);

        routingContext.response()
            .setStatusCode(code)
            .putHeader("content-type", "application/json; charset=utf-8")
            .end(jsonObject.encodePrettily());
    }

    /**
     * 返回json的方法
     * @param routingContext RoutingContext
     * @param code           int
     * @param msg            String
     * @param data           JsonArray
     */
    private void returnJson(RoutingContext routingContext, int code, String msg, JsonArray data) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.put("code", code).put("msg", msg).put("data", data);

        routingContext.response()
            .setStatusCode(code)
            .putHeader("content-type", "application/json; charset=utf-8")
            .end(jsonObject.encodePrettily());
    }

}
